package org.despair2D.ui.core
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import org.despair2D.control.MouseManager;
	import org.despair2D.control.ZMouseEvent;
	import org.despair2D.core.IFrameListener;
	import org.despair2D.core.ProcessManager;
	import org.despair2D.debug.Logger;
	import org.despair2D.ui.core.Component;
	import org.despair2D.ui.core.FusionComp;
	import org.despair2D.ui.core.IComponent;
	import org.despair2D.ui.core.SpriteAdvance;
	import org.despair2D.ui.events.ManipulateEvent;
	import org.despair2D.ui.Fusion;
	import org.despair2D.ui.Persona;
	import org.despair2D.ui.puppet.IPuppet;
	import org.despair2D.ui.puppet.PuppetCompBase;
	
	import org.despair2D.core.ns_despair;
	use namespace ns_despair;
	
	
final public class UIManager implements IFrameListener
{

	public function UIManager()
	{
		m_stage = ProcessManager.m_stage
		
		this.addMonitor()
		this.initializeUI()
	}
	
	
	
	ns_despair static const PRIORITY:int = 800
	
	
	
	/** 按下的合体 **/
	final ns_despair function get pressedFusion():Fusion
	{
		return getProxy(m_pressedFusionCID) as Fusion;
	}
	
	/** 按下的傀儡 **/
	final ns_despair function get pressedPuppet() : IPuppet
	{
		return getProxy(m_pressedPuppetCID) as IPuppet;
	}
	
	/** 移上的合体 **/
	final ns_despair function get overFusion() : Fusion
	{
		return getProxy(m_overFusionCID) as Fusion;
	}
	
	/** 移上的傀儡 **/
	final ns_despair function get overPuppet() : IPuppet
	{
		return getProxy(m_overPuppetCID) as IPuppet;
	}
	
	/** 正在拖动的组件 **/
	final ns_despair function get draggingObject() : IComponent
	{
		return getProxy(m_draggingCID)
	}
	
	
	
	
	final ns_despair function addMonitor() : void
	{
		m_monitor                =  Persona.m_monitor        =  new Sprite()
		m_monitor.mouseEnabled   =  m_monitor.mouseChildren  =  false
		m_monitor.tabEnabled     =  m_monitor.tabChildren    =  false
		
		m_stage.addChildAt(m_monitor, 0)
	}
	
	final ns_despair function initializeUI() : void
	{
		MouseManager.getInstance().leftButton.addEventListener(ZMouseEvent.MOUSE_PRESS, ____onMouseMessage, false, PRIORITY )
		MouseManager.getInstance().leftButton.addEventListener(ZMouseEvent.MOUSE_RELEASE, ____onMouseMessage, false, PRIORITY )
		MouseManager.getInstance().addEventListener(ZMouseEvent.MOUSE_MOVE, ____onMouseMessage, false, PRIORITY )
		MouseManager.getInstance().addEventListener(ZMouseEvent.DOUBLE_CLICK, ____onMouseMessage, false, PRIORITY )
		MouseManager.getInstance().addEventListener(ZMouseEvent.EXIT_STAGE, ____onMouseMessage, false, PRIORITY )
		
		// 最先获取滑鼠下的对象列表
		ProcessManager.addFrameListener(this, ProcessManager.UI)
	}
	
	
	
	
	/**
	 * 获取组件委托者
	 * @param	cid
	 */
	final ns_despair function getProxy( cid:uint ) : IComponent
	{
		var C:Component = m_registeredCompList[cid];
		return C ? C.m_proxy as IComponent : null;
	}
	
	
	/**
	 * 注册组件
	 * @param	comp
	 */
	ns_despair static function registerComp( comp:Component ) : void
	{
		m_registeredCompList[++m_numRegistered]  =  comp;
		comp.m_cid                               =  m_numRegistered;
	}
	

	/**
	 * 开始拖动
	 * @param	C
	 * @param	lockCenter
	 * @param	bounds		该范围相对于stage
	 * 
	 * @usage	stage尺寸改变需要滑鼠按下拖动，与拖动组件不会同时发生，所以最大拖动范围直接锁定当前舞台尺寸.
	 */
	ns_despair static function startDragComp( C:Component, lockCenter:Boolean = false, bounds:Rectangle = null ) : void
	{
		var stageWidth:Number, stageHeight:Number
		var gp:Point = C.globalPos
		
		if (lockCenter)
		{
			m_draggingOffsetX = m_draggingOffsetY = 0
			C.moveInStage(m_stage.mouseX, m_stage.mouseY)
		}
		
		else
		{
			m_draggingOffsetX  =  m_stage.mouseX - gp.x;
			m_draggingOffsetY  =  m_stage.mouseY - gp.y;
		}

		m_draggingCID  =  C.m_cid
		stageWidth     =  m_stage.stageWidth
		stageHeight    =  m_stage.stageHeight
		
		if (bounds)
		{
			if (bounds.x < 0)
			{
				m_boundsX  =  0
			}
			
			else if (bounds.x < stageWidth)
			{
				m_boundsX = bounds.x
			}
			
			else
			{
				Logger.reportError('UIManager', 'startDragComp', '拖动参数bounds(x)有误 !!')
			}
			
			if (bounds.y < 0)
			{
				m_boundsY  =  0
			}
			
			else if (bounds.y < stageHeight)
			{
				m_boundsY = bounds.y
			}
			
			else
			{
				Logger.reportError('UIManager', 'startDragComp', '拖动参数bounds(y)有误 !!')
			}
			
			if (bounds.right > stageWidth)
			{
				m_boundsWidth = stageWidth - bounds.x
			}
			
			else
			{
				m_boundsWidth = bounds.width
			}
			
			if (bounds.bottom > stageHeight)
			{
				m_boundsHeight = stageHeight - bounds.y
			}
			
			else
			{
				m_boundsHeight = bounds.height
			}
		}
			
		else
		{
			m_boundsX       =  0
			m_boundsY       =  0
			m_boundsWidth   =  m_stage.stageWidth
			m_boundsHeight  =  m_stage.stageHeight
		}
		
		m_draggingInBounds = whetherInBoundsForDrag(m_stage.mouseX, m_stage.mouseY)
		m_stage.addEventListener(MouseEvent.MOUSE_MOVE, ____onDragging);
	}
	
	
	ns_despair static function stopDrag() : void
	{
		m_stage.removeEventListener(MouseEvent.MOUSE_MOVE, ____onDragging)
		
		var C:Component = m_registeredCompList[m_draggingCID];
		
		if (C && C.parent)
		{
			C.dispatchEvent(new ManipulateEvent(ManipulateEvent.STOP_DRAG))
		}
		
		m_startDragging = false
		m_draggingCID = 0
	}
	
	
	ns_despair static function ____onDragging( e:MouseEvent ) : void
	{
		var C:Component = m_registeredCompList[m_draggingCID];
		
		if (!m_startDragging)
		{
			C.dispatchEvent(new ManipulateEvent(ManipulateEvent.START_DRAG))
			m_startDragging = true
		}
		
		if (C.parent)
		{
			var mouseX:Number = e.stageX
			var mouseY:Number = e.stageY
			
			m_draggingInBounds = whetherInBoundsForDrag(mouseX, mouseY)
			if (m_draggingInBounds)
			{
				C.moveInStage(mouseX - m_draggingOffsetX, mouseY - m_draggingOffsetY)
			}
		}
		
		else
		{
			stopDrag();
		}
		
		// 渲染(强制进入下一帧)
		e.updateAfterEvent()
		
		// 执行更新
		ProcessManager.updateAll()
	}
	
	
	private static function whetherInBoundsForDrag( mouseX:Number, mouseY:Number ) : Boolean
	{
		return !Boolean(mouseX < m_boundsX || mouseX > m_boundsX + m_boundsWidth || mouseY < m_boundsY || mouseY > m_boundsY + m_boundsHeight)
	}
	
	
	
	
	ns_despair static var m_registeredCompList:Vector.<Component> = new Vector.<Component>(200000, true);
	
	ns_despair static var m_numRegistered:int;
	
	ns_despair static var cachedPoint:Point = new Point();
	
	ns_despair static var m_draggingInBounds:Boolean
	
	ns_despair static var m_startDragging:Boolean
		
	ns_despair static var m_draggingCID:uint;
	
	ns_despair static var m_draggingOffsetX:Number;
	
	ns_despair static var m_draggingOffsetY:Number;

	ns_despair static var m_boundsX:Number
	
	ns_despair static var m_boundsY:Number
	
	ns_despair static var m_boundsWidth:Number
	
	ns_despair static var m_boundsHeight:Number
		
	ns_despair static var m_stage:Stage

	ns_despair var m_monitor:Sprite
	
	ns_despair var m_objectList:Array
	
	ns_despair var m_overFusionCID:uint;
	
	ns_despair var m_overPuppetCID:uint;
	
	ns_despair var m_pressedFusionCID:uint;
	
	ns_despair var m_pressedPuppetCID:uint;

	
	
	
	final public function update( deltaTime:Number ) : void
	{
		cachedPoint.x  =  m_stage.mouseX
		cachedPoint.y  =  m_stage.mouseY
		m_objectList   =  m_monitor.getObjectsUnderPoint(cachedPoint);
	}
	
	/**
	 * 更新交互状态
	 * @param	type
	 * @param	list
	 * 
	 * @usage	1. 按下合体/傀儡后，弹起时，[ 触发弹起侦听 ]的是之前按下的合体/傀儡，而非当前的.
	 * 			2. LEAVE事件触发条件: 组件仍留在舞台上.
	 */
	final ns_despair function ____onMouseMessage( event:ZMouseEvent ) : void
	{
		var fusion:FusionComp, oldFusion:FusionComp;
		var puppet:PuppetCompBase, oldPuppet:PuppetCompBase;
		var fusionCID:uint, puppetCID:uint, l:int
		var target:DisplayObject
		var parent:DisplayObjectContainer
		var type:String
		
		if (event.type == "exitStage")
		{
			type = ZMouseEvent.MOUSE_MOVE
		}
		
		else
		{
			type = event.type
		}
		
		// debug !!
		//trace(type)
		
		l = m_objectList.length
		while (--l > -1)
		{
			target  =  m_objectList[l];
			parent  =  target.parent
			while (parent && !Boolean(parent is SpriteAdvance))
			{
				parent = parent.parent
			}
			puppet = parent as PuppetCompBase
			
			// 按到非内部组件
			if (!puppet)
			{
				if (type == ZMouseEvent.MOUSE_MOVE && m_overFusionCID > 0)
				{
					oldFusion        =  m_registeredCompList[m_overFusionCID] as FusionComp;
					m_overFusionCID  =  0;
					
					if (oldFusion != null && oldFusion.interactiveZ && oldFusion.stage)
					{
						oldPuppet        =  m_registeredCompList[m_overPuppetCID] as PuppetCompBase;
						m_overPuppetCID  =  0;
						
						// Fusion
						oldFusion.bubble(new ManipulateEvent(ManipulateEvent.LEAVE));
						
						// Puppet
						if (oldPuppet != null && oldPuppet.interactive && oldPuppet.stage)
						{
							oldPuppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.LEAVE));
						}
					}
				}
				
				else if(type == ZMouseEvent.MOUSE_RELEASE && m_pressedFusionCID > 0)
				{
					oldFusion           =  m_registeredCompList[m_pressedFusionCID] as FusionComp;
					m_pressedFusionCID  =  0;

					if (oldFusion != null && oldFusion.interactiveZ)
					{
						oldPuppet           =  m_registeredCompList[m_pressedPuppetCID] as PuppetCompBase;
						m_pressedFusionCID  =  m_pressedPuppetCID = 0;
						
						// Fusion
						oldFusion.bubble(new ManipulateEvent(ManipulateEvent.RELEASE));
						
						// Puppet
						if (oldPuppet != null && oldPuppet.interactive)
						{
							oldPuppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.RELEASE));
						}
					}
					
				}
				
				return;
			}
			
			fusion = puppet.m_fusion
			if (fusion.interactiveZ && puppet.interactive)
			{
				// 停止传播
				event.stopImmediatePropagation()
				
				switch(type)
				{
					case ZMouseEvent.MOUSE_PRESS:
						
						// 「Press」
						m_pressedFusionCID = fusion.m_cid;
						m_pressedPuppetCID = puppet.m_cid;
						
						// Fusion
						fusion.bubble(new ManipulateEvent(ManipulateEvent.PRESS));
						
						// Puppet
						puppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.PRESS));
						
						return;
						
					case ZMouseEvent.MOUSE_RELEASE:
						
						// 「Release」
						fusionCID = m_pressedFusionCID
						puppetCID = m_pressedPuppetCID
						m_pressedFusionCID = m_pressedPuppetCID = 0;
						
						
						oldFusion = m_registeredCompList[fusionCID] as FusionComp;
						
						if (oldFusion != null && oldFusion.interactiveZ)
						{
							oldPuppet = m_registeredCompList[puppetCID] as PuppetCompBase;
							
							// Fusion
							oldFusion.bubble(new ManipulateEvent(ManipulateEvent.RELEASE));
							
							// Puppet
							if (oldPuppet != null && oldPuppet.interactive)
							{
								oldPuppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.RELEASE));
							}
						}
						
						// 「Click」
						if (fusion.m_cid == fusionCID)
						{
							// Fusion
							fusion.bubble(new ManipulateEvent(ManipulateEvent.CLICK));
							
							// Puppet
							if(puppet.m_cid == puppetCID)
							{
								puppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.CLICK));
							}
						}
						
						return;
						
					case ZMouseEvent.MOUSE_MOVE:
					
						fusionCID  =  fusion.m_cid;
						puppetCID  =  puppet.m_cid;
						
						// Fusion
						if (fusionCID != m_overFusionCID)
						{
							oldFusion        =  m_registeredCompList[m_overFusionCID] as FusionComp;
							m_overFusionCID  =  fusionCID;
							
							if (oldFusion != null && oldFusion.interactiveZ && oldFusion.stage)
							{
								oldFusion.bubble(new ManipulateEvent(ManipulateEvent.LEAVE));
							}
							
							fusion.dispatchEvent(new ManipulateEvent(ManipulateEvent.OVER));
						}
						
						// Puppet
						if (puppetCID != m_overPuppetCID)
						{
							oldPuppet        =  m_registeredCompList[m_overPuppetCID] as PuppetCompBase;
							m_overPuppetCID  =  puppetCID;
							
							if (oldPuppet != null && oldPuppet.interactive && oldPuppet.stage)
							{
								oldPuppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.LEAVE));
							}
							
							puppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.OVER));
						}
						
						//「Move」 
						
						// Fusion
						fusion.bubble(new ManipulateEvent(ManipulateEvent.MOVE));
						
						// Puppet
						puppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.MOVE));
						
						return;
					
					case ZMouseEvent.DOUBLE_CLICK:
						
						// Fusion
						fusion.bubble(new ManipulateEvent(ManipulateEvent.DOUBLE_CLICK));
						
						// Puppet
						puppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.DOUBLE_CLICK));
						
						return;
						
					default:
						return;
				}
			}
		}
		
		// Leave None
		if (type == ZMouseEvent.MOUSE_MOVE && m_overFusionCID > 0)
		{
			oldFusion        =  m_registeredCompList[m_overFusionCID] as FusionComp;
			m_overFusionCID  =  0;
			
			if (oldFusion != null && oldFusion.interactiveZ && oldFusion.stage)
			{			
				oldPuppet        =  m_registeredCompList[m_overPuppetCID] as PuppetCompBase;
				m_overPuppetCID  =  0;
				
				// Fusion
				oldFusion.bubble(new ManipulateEvent(ManipulateEvent.LEAVE));
				
				// Puppet
				if (oldPuppet != null && oldPuppet.interactive && oldPuppet.stage)
				{
					oldPuppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.LEAVE));
				}
			}
		}
		
		// Release None
		else if(type == ZMouseEvent.MOUSE_RELEASE && m_pressedFusionCID > 0)
		{
			oldFusion           =  m_registeredCompList[m_pressedFusionCID] as FusionComp;
			m_pressedFusionCID  =  0;
			
			if (oldFusion != null && oldFusion.interactiveZ)
			{			
				oldPuppet           =  m_registeredCompList[m_pressedPuppetCID] as PuppetCompBase;
				m_pressedPuppetCID  =  0;
				
				// Fusion
				oldFusion.bubble(new ManipulateEvent(ManipulateEvent.RELEASE));
				
				// Puppet
				if (oldPuppet != null && oldPuppet.interactive)
				{
					oldPuppet.dispatchEvent(new ManipulateEvent(ManipulateEvent.RELEASE));
				}
			}
		}
	}
	
}
	
}